%%%-------------------------------------------------------------------
%%% @author wukai
%%% @copyright (C) 2017, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 24. 十月 2017 11:07
%%%-------------------------------------------------------------------
-module(mqtt_protocol).
-author("wukai").
-include("../include/mqtt_protocol.hrl").
-export([rec_bin/3]).

-define(WAIT_HEAD, wait_head).
-define(WAIT_LEFT_LEN, wait_left_len).
-define(WAIT_LEFT_BYTES, wait_left_bytes).

rec_bin(Ref, State, Bin) ->
  NBuf = <<(State#state.buf)/binary, Bin/binary>>,
  log:log("current buf len ~p ,", [byte_size(NBuf)]),
  parse_data(Ref, State#state{buf = NBuf}).

%%解析下一个数据段
parse_data(Ref, State) ->
  case State#state.wait_seg of
    ?WAIT_HEAD -> parse_head(Ref, State);
    ?WAIT_LEFT_LEN -> parse_remain_len(Ref, State);
    ?WAIT_LEFT_BYTES -> parse_remain_bytes(Ref, State, State#state.wait_seq_len)
  end.


%%不够两个字节继续等待接收
parse_head(_, State) when byte_size(State#state.buf) < 2 -> State#state{wait_seg = ?WAIT_HEAD};
parse_head(Ref, State) ->
  <<Frame_Type:4, Flag:4, Left/binary>> = State#state.buf,
  if
    Frame_Type < 1 orelse Frame_Type > 14 ->
      lager:log(info, self(), "framehead error  frametype:~p and frameflag", [Frame_Type, Flag]),
      mqtt_process:exit_self();
    true ->
      Frame_name = frame_typename(Frame_Type),
      lager:log(info, self(), "frame_type_name: ~p,frame_flag: ~p", [Frame_name, Flag]),
      NState = State#state{buf = Left, wait_seg = ?WAIT_LEFT_LEN,
        current_frame = #mqtt_frame{frame_type = Frame_Type, frame_flag = Flag}},
      parse_remain_len(Ref, NState)
  end.

parse_remain_len(_, State) when byte_size(State#state.buf) < 1 ->
  State#state{wait_seg = ?WAIT_LEFT_LEN};%%不够1个字节继续等待接收

parse_remain_len(Ref, State) ->
  <<First:1, _:7, Left/binary>> = State#state.buf,
  <<Len:8, _/binary>> = State#state.buf,
  case First of
    1 ->
      Lens = State#state.byteslen ++ [Len],
      parse_remain_len(Ref, State#state{buf = Left, byteslen = Lens});
    0 ->
      Remain_Len = protocol_util:bytes_len(State#state.byteslen ++ [Len]),
      log:log("remain len ~p", [Remain_Len]),
      parse_remain_bytes(Ref, State#state{buf = Left}, Remain_Len)
  end.

%%不够内容长度继续等待
parse_remain_bytes(_, State, Len) when byte_size(State#state.buf) < Len ->
  State#state{wait_seg = ?WAIT_LEFT_BYTES, wait_seq_len = Len};
parse_remain_bytes(Ref, State, Len) ->
  <<Play:Len/binary, Left/binary>> = State#state.buf,
  Mqtt_Frame = State#state.current_frame#mqtt_frame{left_bytes = Play},
  connected(State#state.connected, Mqtt_Frame#mqtt_frame.frame_type),
  NewState = command:do_command(Mqtt_Frame#mqtt_frame.frame_type, Ref, State, Mqtt_Frame),
  parse_head(Ref, NewState#state{buf = Left}).

frame_typename(1) -> frame_conn;
frame_typename(2) -> frame_conn_ack;
frame_typename(3) -> frame_pub;
frame_typename(4) -> frame_pub_ack;
frame_typename(5) -> frame_pub_rec;
frame_typename(6) -> frame_pub_rel;
frame_typename(7) -> frame_pub_comp;
frame_typename(8) -> frame_sub;
frame_typename(9) -> frame_sub_ack;
frame_typename(10) -> frame_unsub;
frame_typename(11) -> frame_unsub_ack;
frame_typename(12) -> frame_ping;
frame_typename(13) -> frame_ping_ack;
frame_typename(14) -> frame_disconnect;
frame_typename(_) -> error.

%%在发送其它消息如pub sub unsub之前，校验当前链接是否已经传输过connect消息
connected(C, T) when T /= ?FRAME_CONN, C == false ->
  log:log("reconnect error"),
  mqtt_process:exit_self();
%%如果当前消息是链接消息，那么则校验是否已经连接过 如果是重复发连接消息，则关闭此链接
connected(C, T) when T == ?FRAME_CONN, C == true ->
  log:log("reconnect error"),
  mqtt_process:exit_self();
connected(_, _) -> ok.

